The Command Pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.
翻譯年糕:將一個請求封裝成一個物件,讓你可用不同的請求對客戶進行參數化、對請求排隊或記錄請求日誌,以及支援可取消的操作
這是什麼意思?當A要請求B執行任務時,A會呼叫B然後B在完成任務,在這種情況下A需直接和B進行溝通,就像A是老闆,他要交代員工B去做事情一樣,如下圖。我們稱做A為Sender,B為Reciver。
但如果Sander需要Reciver做很多事怎麼辦?就像是老闆非常忙碌,他沒辦法提醒每個人該做什麼事情,那該怎麼辦?這時老闆可以使用備忘錄。他將任務寫在備忘錄上,再經由秘書把任務交給員工完成。
老闆(Sender)將任務封裝成備忘錄(Command),然後秘書(Invoker)再經由備忘錄的工作事項分派任務給員工(Receiver)。如此一來老闆不需要知道是哪個員工執行,只需要秘書回報任務結果即可。看完這兩張簡易的流程圖,我們對於Command Pattern有了初步的了解。
Command Pattern有幾個組成角色
成員 | 功用 |
---|---|
Command | 用來宣告執行操作的interface / abstract class。 |
ConcreteCommand | Command的實體物件,通常會持有Receiver,並呼叫Receiver的功能來完成命令要執行的操作。 |
Receiver(接收者) | 幹活的角色, 命令傳遞到被執行。 |
Invoker(請求者) | 接收並要求執行命令。 |
Client(裝配者) | 建立Command Object,組裝Command Object和Receiver |
註:這邊的Clinet並非客戶端的Client,解釋為裝配者較適合,因為使用命令的Client應該是從Invoker來觸發執行
我們可以用下面的UML圖來了解個角色的位置
由表格對應UML可得知,Invoker為呼叫者,負責要求執行命令;Command為命令的介面或抽象類;ConcreteCommand為各種命令實體繼承Command;Receiver為執行者負責執行命令;Client內定義接收者、命令以及呼叫者,接著將命令交給呼叫者執行。
認識完Command Pattern的角色後,我們將這些角色套用在程式碼內試試看:
Receiver:負責執行命令
class Receiver {
public void action(String str) {
System.out.println(str);
}
}
Command:宣告執行命令的abstract class
abstract class Command{
Receiver receiver;
public Command(Receiver receiver){
this.receiver = receiver;
}
public abstract void execute();
}
ConcreteCommand:Command的實體物件,持有並呼叫Receiver的功能來完成命令要執行的操作
class ConcreteCommand extends Command{
ConcreteCommand(Receiver receiver){
super(receiver);
}
@Override
public void execute(){
receiver.action();
}
}
Invoker:接收並要求執行命令
class Invoker {
private List<Command> commandList = new ArrayList<>();
public void setCommand(Command command){
commandList.add(command);
}
public void executeCommand(){
System.out.println("Invoker call Receiver");
for (Command command :
commandList) {
command.execute();
}
}
}
Client
public class CommandPattern {
public static void main(String[] args) {
// 定義Receiver
Receiver receiver = new Receiver();
// 定義給Invoker的Command(可以多個)
Command command = new ConcreteCommand(receiver);
//定義Invoker
Invoker invoker = new Invoker();
System.out.println("Client call Invoker ...");
// Invoker接收指令
invoker.setCommand(command);
// Invoker執行指令
invoker.executeCommand();
}
}
output
Client call Invoker ...
Invoker call Receiver
Receiver do action
由上述程式碼,應該更加了解Command Pattern執行的流程,如果還對流程有些模糊,可以參考下面時序圖的流程。
現在我們了解也學會了如何使用Command Pattern,接著就回到老闆及員工的範例,我們來請秘書分擔老闆的事情:
class Employee {
public void action(String str) {
System.out.println(str);
}
}
abstract class Command{
Employee employee;
public Command(Employee employee){
this.employee = employee;
}
public abstract void execute();
}
class MeetingCommand extends Command{
MeetingCommand(Employee employee){
super(employee);
}
@Override
public void execute(){
employee.action("Employee do MeetingCommand");
}
}
class PrepareSlideCommand extends Command{
PrepareSlideCommand(Employee employee){
super(employee);
}
@Override
public void execute(){
employee.action("Employee do PrepareSlideCommand");
}
}
class Secretary {
private List<Command> commandList = new ArrayList<>();
public void setCommand(Command command){
commandList.add(command);
}
public void executeCommand(){
System.out.println("Secretary call Receiver");
for (Command command :
commandList) {
command.execute();
}
}
}
public class CommandPattern {
public static void main(String[] args) {
// 定義Receiver
Employee employee = new Employee();
// 定義給Invoker的Command(可以多個)
Command prepareSlide=new PrepareSlideCommand(employee);
Command meeting=new MeetingCommand(employee);
//定義Invoker
Secretary secretary=new Secretary();
System.out.println("Boss call Secretary ...");
// Invoker接收指令
secretary.setCommand(prepareSlide);
secretary.setCommand(meeting);
// Invoker執行指令
secretary.executeCommand();
}
}
output
Boss call Secretary ...
Secretary call Receiver
Employee do PrepareSlideCommand
Employee do MeetingCommand
如此一來老闆就不需要盯著每一個員工做事,他只需要將事情交代給秘書,由秘書去叫員工把事情做好即可。
優點 | 缺點 |
---|---|
- 降低耦合度 - 易擴充 - 容易設計組合命令 | - 若系統需要非常大量的Command,會影像到Command Pattern的使用 |
請求以命令的形式封裝在物件中,並傳給調用對象。調用對象尋找可處理該命令的適合對象,並將命令傳給合適的對象執行。
初探設計模式 - 命令模式 ( Command Pattern )
命令模式
簡說設計模式——命令模式